package org.mmtk.plan.scale;

import org.mmtk.plan.scale.utils.ArayHelper;
import org.mmtk.utility.Log;
import org.mmtk.utility.options.Options;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.ObjectReference;

@Uninterruptible
public class ScaleScanner extends ScaleMarkSweepSpace.Scanner {
	private int colors; // how many different colors can be in heap
	private long[] coloredObjects1;
	private long[] coloredObjects2;
	private long[] coloredObjects3;
	private long[] coloredObjects4;
	private long[] coloredObjects5;
	private long totalObjects;	//from the first scan
	private long totalObjectsCounter;	//always
	protected long liveObjects; //from the second scan
	protected long liveObjectsCounter; //always
	protected long unmarkedObjects;
	protected long markedObjects;
	protected int prevMarkColor;
	protected int scan;

	private void init() {
		ArayHelper.zeroArray(coloredObjects1);
		ArayHelper.zeroArray(coloredObjects2);
		ArayHelper.zeroArray(coloredObjects3);
		ArayHelper.zeroArray(coloredObjects4);
		ArayHelper.zeroArray(coloredObjects5);
		this.totalObjects = 0;
		this.liveObjects = 0;	
		this.unmarkedObjects = 0;
		this.markedObjects = 0;
	}
	
	public void release() {
		Log.write("ScaleScanner.release, totalObjects: ");
		Log.write(totalObjects);
		Log.write(", liveObjects: ");
		Log.writeln(liveObjects);		
		init();
	}
	
	public ScaleScanner() {
		colors = (1 << Options.colorMarkSweepMarkBits.getValue());
		coloredObjects1 = new long[colors];
		coloredObjects2 = new long[colors];
		coloredObjects3 = new long[colors];
		coloredObjects4 = new long[colors];
		coloredObjects5 = new long[colors];
		
		init();
	}

	/**
	 * Called for live objects in heap
	 * 
	 * always return false to signify that live objects remains live after this
	 * operation
	 */
	public boolean scanCell(ObjectReference objRef, int color) {
		if (color < 0 || color >= colors) {
			//sanity
			if (Options.scaleDebug.getValue() > 3) {
				Log.write("ScaleScanner.scanCell, non existing color !!! objRef=");
				Log.write(objRef);
				Log.write(", color=");
				Log.writeln(color);
				Log.flush();
				VM.objectModel.dumpObject(objRef);
				Log.flush();
			}
			return false;
		} 
		
		this.totalObjectsCounter++;
		switch(scan) {
		case 1: 
		{
			coloredObjects1[color]++;
			break;
		}
		case 2: 
		{
			coloredObjects2[color]++;
			break;
		}
		case 3: 
		{
			coloredObjects3[color]++;
			break;
		}
		case 4: 
		{
			coloredObjects4[color]++;
			break;
		}
		case 5: 
		{
			coloredObjects5[color]++;
			break;
		}
		}
		processCell(objRef, color);
		return true;
	}

	public void start(int prevMarkColor, int scan) {
		this.prevMarkColor = prevMarkColor;
		this.scan = scan;
		this.totalObjectsCounter = 0;
		this.liveObjectsCounter = 0;
		
		if(scan == 1) {
			this.unmarkedObjects = 0;
		}
		
		if (Options.scaleDebug.getValue() > 3) {
			Log.write("ScaleScanner.start, prevMarkColor=");
			Log.write(prevMarkColor);
			Log.write(", scan=");
			Log.writeln(scan);
			Log.flush();
		}
	}

	public void end() {
		if (Options.scaleDebug.getValue() > 3) {
			Log.writeln("ScaleScanner.end, coloredObjects:");
			for (int i = 0; i < colors; i++) {
				Log.write(i);
				Log.write(" ");
				switch(scan) {
				case 1: 
				{
					Log.writeln(coloredObjects1[i]);
					break;
				}
				case 2: 
				{
					Log.writeln(coloredObjects2[i]);
					break;
				}
				case 3: 
				{
					Log.writeln(coloredObjects3[i]);
					break;
				}
				case 4: 
				{
					Log.writeln(coloredObjects4[i]);
					break;
				}
				case 5: 
				{
					Log.writeln(coloredObjects5[i]);
					break;
				}
				}
			}
			Log.flush();
			
			Log.write("ScaleScanner.summary: totalObjectsCounter=");
			Log.write(totalObjectsCounter);
			if(scan == 1) {
				Log.write(", unmarkedObjects: ");
				Log.write(unmarkedObjects);
				this.totalObjects = this.totalObjectsCounter; //remember the number of objects counter in the first scan
			} else {
				if(this.totalObjects != this.totalObjectsCounter) {
					//compare to the remembered in the first scan
					Log.write(" !!! totalObjects changed");
				}
			}
			if(scan == 2) {
				Log.write(", liveObjectsCounter: ");
				Log.write(liveObjectsCounter);
				this.liveObjects = this.liveObjectsCounter; //remember the number of objects counter in the first scan				
			}
			if(scan == 4) {
				Log.write(", markedObjects=");
				Log.write(markedObjects);
				Log.write(", unmarkedObjects=");
				Log.write(unmarkedObjects);			
			}
		}
		Log.write("\n");
	}

	public void processCell(ObjectReference objRef, int color) {
		//check there no unneeded mark colors in heap - first phase
		if(scan==1 && color != 0) {
			Scale.scaleSpace.writeAllocState(objRef);
			if(unmarkedObjects == 0) {
				Log.writeln("ScaleScanner.processCell, !!! first scan had to unmark objects");
			}
			this.unmarkedObjects++;
			return;
		}
		
		if(scan==2 ) {
			if(color != prevMarkColor && color != 0) {
				if (Options.scaleDebug.getValue() > 3) {
					Log.write("ScaleScanner.processCell, !!! wrongly marked objRef=");
					Log.flush();
					VM.objectModel.dumpObject(objRef);
					Log.flush();
				}
				return;
			}
			if(color == prevMarkColor) {
				this.liveObjectsCounter++;
			}
			return;
		}
		
		//mark objects that failed to be reached in the fixing phase
		if(scan == 4 ) {
			if(color == prevMarkColor || color == prevMarkColor - 1) {
				if (Options.scaleDebug.getValue() > 3) {
					Log.write("ScaleScanner.processCell, check trace wrongly marked objRef=");
					Log.flush();
					VM.objectModel.dumpObject(objRef);
					Log.flush();
				}
				Scale.scaleSpace.markObject(objRef);
				markedObjects++;
			}	
		}
	}
}
